package top.bug.blog.schedule;

import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Service;
import top.bug.blog.enums.ErrorCode;
import top.bug.blog.exception.BussinessException;

import javax.annotation.Resource;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author <a href="https://github.com/jiusi66">九思.</a>
 * @email 10837907@qq.com
 * @from <a href="https://www.52bug.top">递归茶馆-www.52bug.top</a>
 * @description 定时任务管理器-实现增删改查等逻辑
 */
@Service
@Slf4j
public class SchedulerManage {

    /**
     * 注入Quartz调度器
     */
    @Resource
    private Scheduler scheduler;

    /**
     * 创建并调度一个定时任务
     */
    public void scheduleJob(Map<String, Object> requestMap) {
        try {
            // 1. 从请求体中获取必要参数
            String jobName = (String) requestMap.get("jobName");
            String jobGroup = (String) requestMap.get("jobGroup");
            // 接收类全路径名
            String jobClassFullName = (String) requestMap.get("jobClass");
            String cronExpression = (String) requestMap.get("jobCron");
            // 2. 参数校验（确保关键参数不为空）
            if (jobName == null || jobGroup == null || jobClassFullName == null || cronExpression == null) {
                throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
            }
            // 校验cron表达式是否有效
            if (!isValidCronExpression(cronExpression)) {
                throw new BussinessException(ErrorCode.PARAM_IS_INVALID, "Cron表达式无效");
            }
            // 3. 构建JobDataMap（可选，用于传递自定义参数）
            JobDataMap jobDataMap = new JobDataMap();
            // 假设请求体中有一个名为"jobData"的Map，包含要传递的参数
            Map<String, Object> requestJobData = (Map<String, Object>) requestMap.get("jobData");
            if (requestJobData != null) {
                jobDataMap.putAll(requestJobData);
            }
            // 4. 创建任务 核心：通过反射获取Job类的Class对象
            Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(jobClassFullName);
            // 1. 定义JobDetail，关联Job类 使用动态加载的Class
            JobDetail jobDetail = JobBuilder.newJob(jobClass)
                    .withIdentity(jobName, jobGroup)
                    .usingJobData(jobDataMap)
                    .storeDurably(true)
                    .build();
            // 2. 定义Cron触发器（通常triggerName和group可以与job相同，也可自定义）
            TriggerKey triggerKey = new TriggerKey(jobName, jobGroup);
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(triggerKey)
                    .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                    .forJob(jobDetail)
                    .build();
            // 3. 将Job和Trigger交由Scheduler调度
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            log.error("任务创建失败", e);
            throw new BussinessException(ErrorCode.SCHEDULER_ERROR, e.getMessage());
        } catch (ClassNotFoundException e) {
            // 专门处理类找不到的异常
            log.error("指定的任务类不存在或无法加载", e);
            throw new BussinessException(ErrorCode.SCHEDULER_ERROR, "指定的任务类不存在或无法加载: " + e.getMessage());
        } catch (ClassCastException e) {
            // 处理类路径名对应的类不是Job类型的情况
            log.error("指定的类未实现org.quartz.Job接口", e);
            throw new BussinessException(ErrorCode.SCHEDULER_ERROR, "指定的类未实现org.quartz.Job接口");
        } catch (Exception e) {
            // 捕获其他未知异常
            log.error("创建任务时发生未知错误", e);
            throw new BussinessException(ErrorCode.SCHEDULER_ERROR, "创建任务时发生未知错误: " + e.getMessage());
        }
    }

    /**
     * 立即触发执行一次某个任务
     *
     * @param jobName  任务名
     * @param jobGroup 任务组
     */
    public void triggerJob(String jobName, String jobGroup) {
        JobKey jobKey = new JobKey(jobName, jobGroup);
        try {
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            throw new BussinessException(ErrorCode.SCHEDULER_ERROR, "任务触发失败: " + e.getMessage());
        }
    }

    /**
     * 暂停某个任务
     *
     * @param jobName  任务名
     * @param jobGroup 任务组
     */
    public void pauseJob(String jobName, String jobGroup) {
        JobKey jobKey = new JobKey(jobName, jobGroup);
        try {
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            log.error("暂停失败", e);
            throw new BussinessException(ErrorCode.FAIL);
        }
    }

    /**
     * 恢复某个暂停的任务
     *
     * @param jobName  任务名
     * @param jobGroup 任务组
     */
    public void resumeJob(String jobName, String jobGroup) {
        JobKey jobKey = new JobKey(jobName, jobGroup);
        try {
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            log.error("恢复失败", e);
            throw new BussinessException(ErrorCode.FAIL);
        }
    }

    /**
     * 删除某个任务（会删除JobDetail和关联的Trigger）
     *
     * @param jobName  任务名
     * @param jobGroup 任务组
     */
    public void deleteJob(String jobName, String jobGroup) {
        JobKey jobKey = new JobKey(jobName, jobGroup);
        try {
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            log.error("删除失败", e);
            throw new BussinessException(ErrorCode.FAIL);
        }
    }

    /**
     * 更新任务：同时更新Cron表达式和JobDataMap参数
     *
     * @param jobName           任务名
     * @param jobGroup          任务组
     * @return 返回更新后的触发器下一次触发时间，可用于验证和提示
     */
    public Date updateJob(String jobName, String jobGroup, Map<String, Object> requestMap) {
        try {
            String newCronExpression = (String) requestMap.get("newCronExpression");
            String newDescription = (String) requestMap.get("description");

            if (newCronExpression == null) {
                throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE, "cron表达式为空！");
            }

            // 校验cron表达式是否有效
            if (!isValidCronExpression(newCronExpression)) {
                throw new BussinessException(ErrorCode.PARAM_IS_INVALID, "Cron表达式无效");
            }

            // 获取新的参数Map（可选）
            Map<String, Object> newJobDataMap = (Map<String, Object>) requestMap.get("jobData");

            // 1. 构建JobKey和TriggerKey
            JobKey jobKey = new JobKey(jobName, jobGroup);
            TriggerKey triggerKey = new TriggerKey(jobName, jobGroup);

            // 2. 校验任务和触发器是否存在
            if (!scheduler.checkExists(jobKey)) {
                throw new BussinessException("要更新的任务不存在: " + jobKey);
            }
            if (!scheduler.checkExists(triggerKey)) {
                throw new BussinessException("要更新的触发器不存在: " + triggerKey);
            }

            // 3. 获取旧的JobDetail和Trigger
            JobDetail oldJobDetail = scheduler.getJobDetail(jobKey);
            CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            // 4. 创建全新的 JobBuilder，而不是从旧的获取
            JobBuilder jobBuilder = JobBuilder.newJob(oldJobDetail.getJobClass())
                    .withIdentity(jobKey)
                    .storeDurably(oldJobDetail.isDurable());

            // 处理 JobDataMap - 完全替换
            JobDataMap finalJobDataMap;
            if (newJobDataMap != null && !newJobDataMap.isEmpty()) {
                // 如果传入了新的JobDataMap，则完全替换旧的
                finalJobDataMap = new JobDataMap();
                finalJobDataMap.putAll(newJobDataMap);
            } else {
                // 如果没有传入新的JobDataMap，则保持原有的参数
                finalJobDataMap = new JobDataMap();
                // 复制旧的
                finalJobDataMap.putAll(oldJobDetail.getJobDataMap());
            }

            // 构建新的JobDetail
            JobDetail newJobDetail = jobBuilder
                    .withDescription(newDescription != null ? newDescription : oldJobDetail.getDescription())
                    .usingJobData(finalJobDataMap)
                    .build();

            // 5. 构建新的Trigger（也要处理Trigger的JobDataMap）
            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger()
                    .withIdentity(triggerKey)
                    .forJob(jobKey);

            // 如果Trigger也有JobDataMap，也需要处理
            CronTrigger newTrigger = triggerBuilder
                    .withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
                    // 确保Trigger也使用新的JobDataMap
                    .usingJobData(finalJobDataMap)
                    .build();

            // 6. 删除旧的任务和触发器，然后重新添加
            scheduler.pauseTrigger(triggerKey);
            scheduler.unscheduleJob(triggerKey);
            scheduler.deleteJob(jobKey);

            // 添加新的JobDetail和Trigger
            scheduler.addJob(newJobDetail, true);
            Date nextFireTime = scheduler.scheduleJob(newTrigger);

            // 7. 修改完成后暂停任务
            scheduler.pauseJob(jobKey);

            return nextFireTime;
        } catch (SchedulerException e) {
            log.error("更新任务时发生错误", e);
            throw new BussinessException(ErrorCode.FAIL);
        }
    }

    /**
     * 获取所有任务的信息（包括状态、上次执行时间、下次执行时间等）
     *
     * @return 任务信息列表
     */
    public List<Map<String, Object>> getAllJobDetails() {
        List<Map<String, Object>> jobDetailsList = new ArrayList<>();
        try {
            // 遍历所有的任务组
            for (String groupName : scheduler.getJobGroupNames()) {
                // 遍历组内所有JobKey
                for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
                    Map<String, Object> jobDetails = new HashMap<>();
                    JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);

                    jobDetails.put("jobName", jobKey.getName());
                    jobDetails.put("jobGroup", jobKey.getGroup());
                    jobDetails.put("jobClass", jobDetail.getJobClass().getName());
                    jobDetails.put("description", jobDetail.getDescription());
                    jobDetails.put("jobDataMap", jobDetail.getJobDataMap());

                    // 通常一个Job对应一个Trigger，这里取第一个
                    if (!triggers.isEmpty()) {
                        Trigger trigger = triggers.get(0);
                        jobDetails.put("triggerName", trigger.getKey().getName());
                        jobDetails.put("triggerGroup", trigger.getKey().getGroup());
                        jobDetails.put("triggerState", scheduler.getTriggerState(trigger.getKey()).toString());
                        jobDetails.put("nextFireTime", trigger.getNextFireTime());
                        jobDetails.put("previousFireTime", trigger.getPreviousFireTime());

                        if (trigger instanceof CronTrigger) {
                            jobDetails.put("cronExpression", ((CronTrigger) trigger).getCronExpression());
                        }
                    }
                    jobDetailsList.add(jobDetails);
                }
            }
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
        return jobDetailsList;
    }

    /**
     * 获取cron表达式未来的执行时间
     *
     * @param cronExpression cron表达式
     * @param count          需要获取的未来执行次数
     * @return 未来执行时间的LocalDateTime列表
     * @throws ParseException 如果cron表达式格式无效
     */
    public List<String> getNextExecutionTimes(String cronExpression, int count) {
        List<LocalDateTime> executionTimes = null;
        if (!isValidCronExpression(cronExpression)) {
            throw new BussinessException(ErrorCode.PARAM_IS_INVALID, "cron表达式有误！");
        }
        try {
            // 验证并解析cron表达式
            CronExpression cron = new CronExpression(cronExpression);
            executionTimes = new ArrayList<>();

            // 从当前时间开始计算
            Date startTime = new Date();

            for (int i = 0; i < count; i++) {
                // 获取下一个有效执行时间
                Date nextTime = cron.getNextValidTimeAfter(startTime);
                if (nextTime == null) {
                    break; // 如果已经没有下一次执行时间，则退出循环
                }
                // 将Date转换为LocalDateTime并加入列表
                LocalDateTime nextLocalDateTime = nextTime.toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDateTime();
                executionTimes.add(nextLocalDateTime);

                // 将当前计算时间设置为刚找到的执行时间，以便计算下一次
                startTime = nextTime;
            }
        } catch (ParseException e) {
            log.error("解析cron表达式时发生错误", e);
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        // 日期格式化
        return executionTimes.stream()
                .map(date -> date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .collect(Collectors.toList());
    }

    /**
     * 验证cron表达式是否有效
     *
     * @param cronExpression cron表达式
     * @return 有效返回 true，无效返回 false
     */
    public static boolean isValidCronExpression(String cronExpression) {
        try {
            new CronExpression(cronExpression);
            return true;
        } catch (ParseException e) {
            return false;
        }
    }
}